package javaer.starter.core.parser;

import javaer.starter.base.domain.dto.DependencyDTO;
import javaer.starter.base.domain.dto.ModuleDTO;
import javaer.starter.base.domain.dto.ModuleDependencyDTO;
import javaer.starter.base.domain.dto.NewProjectDefinition;
import javaer.starter.base.service.DependencyService;
import javaer.starter.base.service.ModuleService;
import javaer.starter.constant.ModuleDependTypeEnum;
import javaer.starter.constant.SymbolEnum;
import javaer.starter.core.metadata.DistributionManagement;
import javaer.starter.core.metadata.PomXml;
import org.apache.commons.collections.CollectionUtils;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author yangzhen
 * @date 2021/11/17 16:01
 * @since 1.0.0
 */
public abstract class AbstractPomXmlParser implements IPomXmlParser {

    @Resource
    protected DependencyService dependencyService;

    @Resource
    protected ModuleService moduleService;

    /**
     * 子类重写方法，构建pom信息
     *
     * @param projectDefinition
     * @param module
     * @return
     */
    protected abstract PomXml initPom(NewProjectDefinition projectDefinition, ModuleDTO module);

    /**
     * 获取模块的包
     *
     * @param projectDefinition
     * @param module
     * @return
     */
    protected String getModulePackage(NewProjectDefinition projectDefinition, ModuleDTO module) {
        return projectDefinition.getBasePackage() + SymbolEnum.POINT.getSymbol() + module.getModuleId().toLowerCase();
    }

    /**
     * 获取artifactId
     *
     * @param projectDefinition
     * @param module
     * @return
     */
    @Override
    public String getArtifactId(NewProjectDefinition projectDefinition, ModuleDTO module) {
        return projectDefinition.getArtifactId() + SymbolEnum.HORIZONTAL_LINE.getSymbol() + module.getModuleId().toLowerCase();
    }

    /**
     * 构建pom.xml
     *
     * @param projectDefinition
     * @param module
     * @return
     * @throws Exception
     */
    protected PomXml doBuild(NewProjectDefinition projectDefinition, ModuleDTO module) {

        // 初始化pom
        PomXml pomXml = initPom(projectDefinition, module);
        // 设置parent标签
        pomXml.setParent(getParent(projectDefinition));
        // 设置properties标签
        pomXml.setProperties(getProperties(projectDefinition));
        // 设置module标签
        pomXml.setModules(getModules(projectDefinition, module));
        // 设置依赖的module
        pomXml.setDependencyModules(getDependencyModules(projectDefinition, module));
        // 设置依赖
        pomXml.setDependencies(getDependencies(projectDefinition, module));
        // 设置distributionManagement标签
        pomXml.setDistributionManagement(getDistributionManagement(projectDefinition));
        // 设置启动类
        if (pomXml.getImStarterPom()) {
            pomXml.setMainClass(pomXml.getModulePackage() + "." + "Application");
        }

        return pomXml;
    }

    /**
     * 获取module必须的依赖
     *
     * @param projectDefinition
     * @param moduleId
     * @return
     */
    private Set<String> getDefaultDependencyIds(NewProjectDefinition projectDefinition, String moduleId) {

        List<ModuleDependencyDTO> moduleDependencyList = moduleService.findModuleDependencyByTypeIdAndModuleIdAndDepType(projectDefinition.getTypeId(), moduleId, ModuleDependTypeEnum.JAR.getValue());
        if (CollectionUtils.isNotEmpty(moduleDependencyList)) {
            Set<String> dependencyIds = new HashSet<>();
            for (ModuleDependencyDTO moduleDependency : moduleDependencyList) {
                dependencyIds.add(moduleDependency.getDepId());
            }
            return dependencyIds;
        }
        return null;
    }

    /**
     * 获取pom中父子关系的module标签的内容
     *
     * @param projectDefinition
     * @param module
     * @return
     */
    public Set<String> getModules(NewProjectDefinition projectDefinition, ModuleDTO module) {
        return null;
    }

    /**
     * 获取依赖的模块
     *
     * @param projectDefinition
     * @param module
     * @return
     */
    protected Set<String> getDependencyModules(NewProjectDefinition projectDefinition, ModuleDTO module) {
        List<ModuleDependencyDTO> moduleList = moduleService.findModuleDependencyByTypeIdAndModuleIdAndDepType(projectDefinition.getTypeId(), module.getModuleId(), ModuleDependTypeEnum.MODULE.getValue());
        if (CollectionUtils.isNotEmpty(moduleList)) {
            return moduleList.stream().map(v -> projectDefinition.getArtifactId() + SymbolEnum.HORIZONTAL_LINE.getSymbol() + v.getDepId().toLowerCase())
                    .collect(Collectors.toSet());
        }
        return null;
    }

    /**
     * 解析模块的依赖
     * 1、模块默认包含的依赖是必选依赖
     * 2、用户选择依赖
     * 3、依赖关联依赖
     * 4、依赖排除
     * 5、去重依赖
     *
     * @param projectDefinition
     * @param module
     * @return
     */
    public Set<DependencyDTO> getDependencies(NewProjectDefinition projectDefinition, ModuleDTO module) {

        // 去重依赖id
        Set<String> mergeDependencyIds = new HashSet<>();

        // module默认依赖
        Set<String> mustDependencyIdSet = getDefaultDependencyIds(projectDefinition, module.getModuleId());

        // 用户选择的依赖
        Set<String> dependencyIdSet = getSelectedDependencyIds(projectDefinition, module.getModuleId());

        if (CollectionUtils.isNotEmpty(mustDependencyIdSet)) {
            mergeDependencyIds.addAll(mustDependencyIdSet);
        }

        if (CollectionUtils.isNotEmpty(dependencyIdSet)) {
            mergeDependencyIds.addAll(dependencyIdSet);
        }

        if (CollectionUtils.isEmpty(mergeDependencyIds)) {
            return null;
        }

        // 查询所有依赖
        Set<DependencyDTO> dependencyDTOS = dependencyService.findByTypeIdAndDepIds(projectDefinition.getTypeId(), mergeDependencyIds);
        return dependencyDTOS;
    }

    /**
     * 用户选择的依赖id
     *
     * @param projectDefinition
     * @param moduleId
     * @return
     */
    private Set<String> getSelectedDependencyIds(NewProjectDefinition projectDefinition, String moduleId) {
        return Optional.ofNullable(projectDefinition.getModuleDependencyMapping()).map(map -> map.get(moduleId)).orElse(null);
    }

    protected DistributionManagement getDistributionManagement(NewProjectDefinition projectDefinition) {
        return null;
    }

}